﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Xml;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Epi;
using Epi.ImportExport;
using Epi.ImportExport.Filters;
using Epi.ImportExport.ProjectPackagers;
using Menafrinet.ViewModel;
using Menafrinet.View.Controls;

namespace Menafrinet.View.Controls
{
    /// <summary>
    /// Interaction logic for TransmitPanel.xaml
    /// </summary>
    public partial class TransmitPanel : UserControl
    {
        public delegate void ExportCompletedHandler(ExportInfo info);

        private LabExportTypes LabExportType { get; set; }

        private DataHelper DataHelper
        {
            get
            {
                return (this.DataContext as DataHelper);
            }
        }

        public TransmitPanel()
        {
            InitializeComponent();
        }

        private void btnExport_Click(object sender, RoutedEventArgs e)
        {
            // Do not allow package creation if we're already transmitting or in the process of merging
            if (!(Application.Current as App).CanTransmit)
            {
                MessageBox.Show("Cannot transmit data while merging.", "Error", MessageBoxButton.OK, MessageBoxImage.Exclamation);
                return;
            }

            // Do not allow package creation if we're the NRL and we haven't specified destination options
            if (Properties.Settings.Default.SystemLevel == 1 && cmbDestinationType.SelectedIndex == -1)
            {
                MessageBox.Show("Please select a destination type for this package before proceeding.", "Message", MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }
            else if (Properties.Settings.Default.SystemLevel == 1 && !(cmbDestinationType.SelectedItem is ViewModel.Locations.MoH) && cmbDestination.SelectedItem == null)
            {
                MessageBox.Show("Please select a destination site for this package before proceeding.", "Message", MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            if (DataHelper.ContainsInvalidEpids && DataHelper.ContainsDuplicateCases)
            {
                MessageBox.Show("One or more cases have invalid and/or duplicate EPIDs. Please correct all records with invalid EPIDs (highlighted in red) and duplicate EPIDs (highlighted in yellow) before proceeding.", "Message", MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }
            else
            {
                if (DataHelper.ContainsInvalidEpids)
                {
                    MessageBox.Show("One or more cases have invalid EPIDs. Please correct these records (highlighted in red) before proceeding.", "Message", MessageBoxButton.OK, MessageBoxImage.Information);
                    return;
                }

                if (DataHelper.ContainsDuplicateCases)
                {
                    MessageBox.Show("Duplicate EPIDs were detected. Please correct these records (highlighted in yellow) before proceeding.", "Message", MessageBoxButton.OK, MessageBoxImage.Information);
                    return;
                }
            }

            if (lbiDates.IsSelected == true && (!datePicker1.SelectedDate.HasValue || !datePicker2.SelectedDate.HasValue))
            {
                MessageBox.Show("Please select a start and end date for records that should be included in this package before proceeding.", "Message", MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }
            else if (lbiDates.IsSelected == true && datePicker1.SelectedDate.HasValue && datePicker2.SelectedDate.HasValue)
            {
                if (datePicker1.SelectedDate.Value > datePicker2.SelectedDate.Value)
                {
                    MessageBox.Show("Please ensure the starting date is less than or equal to the ending date.", "Message", MessageBoxButton.OK, MessageBoxImage.Information);
                    return;
                }
                else if (datePicker1.SelectedDate.Value > DateTime.Now)
                {
                    MessageBox.Show("Please ensure the starting date is less than or equal to today's date.", "Message", MessageBoxButton.OK, MessageBoxImage.Information);
                    return;
                }
                else if (datePicker2.SelectedDate.Value > DateTime.Now)
                {
                    MessageBox.Show("Please ensure the ending date is less than or equal to today's date.", "Message", MessageBoxButton.OK, MessageBoxImage.Information);
                    return;
                }
            }

            txtTransmit1.Text = String.Empty;

            ExportSettings settings = new ExportSettings();
            settings.DateExported = DateTime.Now;

            if (datePicker1.SelectedDate.HasValue)
            {
                settings.StartDate = datePicker1.SelectedDate;
            }

            if (datePicker2.SelectedDate.HasValue)
            {
                settings.EndDate = datePicker2.SelectedDate;
            }

            settings.SourceLanguage = Properties.Settings.Default.Language.ToLower();
            settings.Project = DataHelper.Project;
            settings.ProjectPath = DataHelper.ProjectPath;
            settings.CaseForm = DataHelper.CaseForm;
            settings.Database = DataHelper.Database;

            if (Properties.Settings.Default.SystemLevel == 1)
            {
                if (cmbDestinationType.SelectedItem != null && cmbDestinationType.SelectedItem is ViewModel.Locations.Districts)
                {
                    settings.LabExportType = LabExportTypes.ToDistrict;
                    settings.LabExportDestinationCode = (cmbDestination.SelectedItem as ViewModel.Locations.District).Code;
                }
                else if (cmbDestinationType.SelectedItem != null && cmbDestinationType.SelectedItem is ViewModel.Locations.Regions)
                {
                    settings.LabExportType = LabExportTypes.ToRegion;
                    settings.LabExportDestinationCode = (cmbDestination.SelectedItem as ViewModel.Locations.Region).Code;
                }
                else if (cmbDestinationType.SelectedItem != null && cmbDestinationType.SelectedItem is ViewModel.Locations.MoH)
                {
                    settings.LabExportType = LabExportTypes.ToMoH;
                }
            }
            else
            {
                settings.LabExportType = LabExportTypes.None;
            }

            if (lbiAll.IsSelected == true) settings.ExportType = ExportType.AllRecords;
            if (lbiDates.IsSelected == true) settings.ExportType = ExportType.DateRange;
            if (lbiMonth.IsSelected == true) settings.ExportType = ExportType.LastMonth;
            if (lbiAllSinceLast.IsSelected == true) settings.ExportType = ExportType.AllRecordsSinceLast;

            FileInfo fi = new FileInfo(DataHelper.Project.FilePath);
            settings.OutputDirectory = new DirectoryInfo(System.IO.Path.Combine(fi.Directory.FullName, Properties.Settings.Default.DataExportFolder));

            DateTime dt = DateTime.UtcNow;
            string dateDisplayValue = String.Format(System.Globalization.CultureInfo.CurrentCulture, "{0:s}", dt);
            dateDisplayValue = dateDisplayValue.Replace(':', '-'); // The : must be replaced otherwise the encryption fails

            settings.FileName = Properties.Settings.Default.ApplicationName.ToUpper() + "_" + Properties.Settings.Default.SiteCode.ToUpper() + "_" + Properties.Settings.Default.SystemLevel.ToString() + "_" + dateDisplayValue;

            // Disable appropriate fields
            // TODO: Bind these in XAML to CanMerge/CanTransmit instead of setting in code
            btnExport.IsEnabled = false;
            listboxExportTypes.IsEnabled = false;

            // Set the application's state such that we can't merge or transmit
            (Application.Current as App).CanMerge = false;
            (Application.Current as App).CanTransmit = false;

            this.DataHelper.TaskbarProgressState = System.Windows.Shell.TaskbarItemProgressState.Normal;
            this.DataHelper.TaskbarProgressValue = 0.0;

            // Run the merge process on a background thread.
            BackgroundWorker exportWorker = new BackgroundWorker();
            exportWorker.DoWork += new DoWorkEventHandler(exportWorker_DoWork);
            exportWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(exportWorker_RunWorkerCompleted);
            exportWorker.RunWorkerAsync(settings);
        }

        void exportWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            ExportInfo info = (ExportInfo)e.Result;
            this.Dispatcher.BeginInvoke(new ExportCompletedHandler(UpdateExportInfo), info);
            (Application.Current as App).CanMerge = true;
            (Application.Current as App).CanTransmit = true;

            this.DataHelper.TaskbarProgressState = System.Windows.Shell.TaskbarItemProgressState.None;
            this.DataHelper.TaskbarProgressValue = 1.0;
        }

        void exportWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            ExportSettings settings = (ExportSettings)e.Argument;
            ExportInfo info = this.CreateDataPackage(settings);
            e.Result = info;
        }

        internal void ResetHeight()
        {
            grdHome.UpdateLayout();
            txtTransmit1.Height = Math.Min(260, Math.Max(40, grdHome.ActualHeight - 410));
        }
        
        private void SetProgressBar(double value)
        {
            if (Double.IsInfinity(value)) // problem with Epi Info code, sometimes it comes back as infinity and crashes the app
            {
                value = 0; 
            }
            exportProgressBar.Value = value;
            this.DataHelper.TaskbarProgressValue = value / 100;
        }

        private void SetStatusMessage(string message)
        {
            txtTransmit1.Text = txtTransmit1.Text + DateTime.Now.ToString() + " | " + message + "\n";
        }

        private void CallbackSetProgressBar(double value)
        {
            this.Dispatcher.BeginInvoke(new SetProgressBarDelegate(SetProgressBar), value);
        }

        private void CallbackSetStatusMessage(string message)
        {
            this.Dispatcher.BeginInvoke(new UpdateStatusEventHandler(SetStatusMessage), message);
        }

        public Epi.ImportExport.ProjectPackagers.ExportInfo CreateDataPackage(ExportSettings settings)
        {
            Epi.Fields.Field epidField = settings.CaseForm.Fields["EpidShow"];
            XmlDataPackager xmlDataPackager = new XmlDataPackager(settings.CaseForm, "Menafrinet");
            xmlDataPackager.UpdateProgress += new SetProgressBarDelegate(CallbackSetProgressBar);
            xmlDataPackager.StatusChanged += new UpdateStatusEventHandler(CallbackSetStatusMessage);
            xmlDataPackager.FieldsToNull = new Dictionary<string, List<string>>();
            xmlDataPackager.KeyFields.Add(epidField);
            RowFilters rowFilters = new RowFilters(settings.Database);
            XmlDocument xmlDoc = null;
            ExportInfo info = null;

            List<string> fieldsToNull = new List<string>();

            switch (Properties.Settings.Default.SystemLevel)
            {
                case 0: // We're a district.
                    // Remove all fields from the package related to lab data.
                    // Because field names may change across languages, let's just grab all the fields on pages 2, 3, and 4 and add them to the "remove" list
                    foreach (Epi.Page page in settings.CaseForm.Pages) 
                    {
                        foreach(Epi.Fields.RenderableField field in page.Fields) 
                        {
                            if (field is Epi.Fields.IDataField && page.Name.ToLower().StartsWith("lab") && !fieldsToNull.Contains(field.Name))
                            {
                                //fieldsToNull.Add(field.Name);
                            }
                        }
                    }
                    break;
                case 1: // We're the national reference lab.
                    // Remove data from all page 1 fields except the EPID field, which we need for matching, and
                    // then also remove some additional fields the customer specified which should never go to anyone
                    fieldsToNull = new List<string>() { "METHODSHIPMENT", "SPECIMENTEMP", "PCRRNASEPRESULT", "PCRRNASEPCT", "PCRSPECIESRESULT", "PCRSPECIESCT", "PCRSPECIESINT", "PCRSGSTRESULT", "PCRSGSTCT", "PCRSGSTINT", "PCRFINALINT", "PCRCOMMENTS" };

                    foreach (Epi.Page page in settings.CaseForm.Pages)
                    {
                        foreach (Epi.Fields.RenderableField field in page.Fields)
                        {
                            if (field is Epi.Fields.IDataField && !page.Name.ToLower().StartsWith("lab") && !fieldsToNull.Contains(field.Name) && (!field.Name.Equals("EpidShow") && !field.Name.Equals("EPIDNUMBER")))
                            {
                                //fieldsToNull.Add(field.Name);
                            }
                        }
                    }
                    break;
                case 2: // we're a region
                    // Should never get here... Region has no transmission arrow in the data flow model
                    break;
                case 3: // we're an MoH
                    break;
                case 4: // we're Menafrinet
                    // Should never get here... what is Menafrinet going to transmit?? Data flow model does not include going anywhere from this level.
                    break;
            }

            xmlDataPackager.FieldsToNull.Add(settings.CaseForm.Name, fieldsToNull);

            string compressedText = String.Empty;
            FileInfo exportFileInfo = new FileInfo(System.IO.Path.Combine(settings.OutputDirectory.FullName, settings.FileName + ".edp7"));

            FileInfo fi = new FileInfo(settings.ProjectPath);
            DirectoryInfo di = fi.Directory;

            xmlDataPackager.Filters = new Dictionary<string, RowFilters>();

            if (settings.LabExportType == LabExportTypes.ToDistrict)
            {
                TextRowFilterCondition tfc = new TextRowFilterCondition("[DistCode] = @DistCode", "DistCode", "@DistCode", settings.LabExportDestinationCode);
                tfc.Description = "DistCode is equal to " + settings.LabExportDestinationCode;
                rowFilters.Add(tfc);
            }
            else if (settings.LabExportType == LabExportTypes.ToRegion)
            {
                TextRowFilterCondition tfc = new TextRowFilterCondition("[RegCode] = @RegCode", "RegCode", "@RegCode", settings.LabExportDestinationCode);
                tfc.Description = "RegCode is equal to " + settings.LabExportDestinationCode;
                rowFilters.Add(tfc);
            }

            switch (settings.ExportType)
            {
                case ExportType.AllRecordsSinceLast:
                    // arbitrary, test this on first run to see what happens when date shows up as 1/1/0001
                    if (Properties.Settings.Default.DateLastExport.Year > 1920)
                    {
                        DateTime lastExport = Properties.Settings.Default.DateLastExport;
                        DateTimeRowFilterCondition sinceLastFc = new DateTimeRowFilterCondition("[LastSaveTime] >= @LastSaveTime", "LastSaveTime", "@LastSaveTime", lastExport);
                        sinceLastFc.Description = "Last save time is greater than or equal to " + lastExport.ToString();
                        rowFilters.Add(sinceLastFc);
                    }
                    xmlDataPackager.Filters.Add(settings.CaseForm.Name, rowFilters);
                    break;
                case ExportType.LastMonth:
                    DateTime lastMonth = DateTime.Today.AddDays(-30);
                    DateRowFilterCondition dfc = new DateRowFilterCondition("[LastSaveTime] >= @LastSaveTime", "LastSaveTime", "@LastSaveTime", lastMonth);
                    dfc.Description = "Last save time is greater than or equal to " + lastMonth.ToString();
                    rowFilters.Add(dfc);
                    xmlDataPackager.Filters.Add(settings.CaseForm.Name, rowFilters);

                    //txtTransmit1.Text = "'Mois' sélectionné \nExport terminée avec succès. \nRecords emballés: " + info.TotalRecordsPackaged;

                    break;
                case ExportType.DateRange:
                    if (!settings.StartDate.HasValue || !settings.EndDate.HasValue)
                    {
                        //txtTransmit1.Text = "ERREUR: Date de Début et Date de Fin ne peut pas être vide.";
                        return info;
                    }
                    else if (settings.StartDate.Value > settings.EndDate.Value)
                    {
                        return info;
                    }

                    DateTime newUpper = new DateTime(settings.EndDate.Value.Year, settings.EndDate.Value.Month, settings.EndDate.Value.Day, 23, 59, 59);

                    DateTimeRowFilterCondition lower = new DateTimeRowFilterCondition("[LastSaveTime] >= @LastSaveTime1", "LastSaveTime", "@LastSaveTime1", settings.StartDate);
                    DateTimeRowFilterCondition upper = new DateTimeRowFilterCondition("[LastSaveTime] <= @LastSaveTime2", "LastSaveTime", "@LastSaveTime2", newUpper);

                    lower.Description = "Last Save Time >= lower";
                    upper.Description = "Last Save Time <= upper";

                    lower.Description = "Last save time is greater than or equal to " + settings.StartDate.Value.ToString();
                    lower.Description = "Last save time is less than or equal to " + newUpper.ToString();

                    rowFilters.Add(lower);
                    rowFilters.Add(upper);

                    xmlDataPackager.Filters.Add(settings.CaseForm.Name, rowFilters);
                    break;
                default:
                    xmlDataPackager.Filters.Add(settings.CaseForm.Name, rowFilters);
                    break;
            }

            try
            {
                xmlDoc = xmlDataPackager.PackageForm();

                XmlAttribute codeAttribute = xmlDoc.CreateAttribute("SiteCode");
                XmlAttribute levelAttribute = xmlDoc.CreateAttribute("SystemLevel");

                codeAttribute.Value = Properties.Settings.Default.SiteCode;
                levelAttribute.Value = Properties.Settings.Default.SystemLevel.ToString();

                xmlDoc.ChildNodes[0].Attributes.Append(codeAttribute);
                xmlDoc.ChildNodes[0].Attributes.Append(levelAttribute);

                info = xmlDataPackager.ExportInfo;

                if (!settings.SourceLanguage.ToLower().Equals("en-us"))
                {
                    CallbackSetStatusMessage(String.Format("Translating field names in data package from {0}...", settings.SourceLanguage));
                    xmlDoc = Core.Common.TranslateDataPackageFieldsToInvariant(xmlDoc, settings.SourceLanguage);
                    CallbackSetStatusMessage("Translation of field names complete.");
                }

                compressedText = ImportExportHelper.Zip(xmlDoc.OuterXml);
                compressedText = "[[EPIINFO7_DATAPACKAGE]]" + compressedText;

                Configuration.EncryptStringToFile(compressedText, exportFileInfo.FullName, string.Empty);
            }
            catch (Exception ex)
            {
                CallbackSetStatusMessage(ex.Message);
                info = new ExportInfo();
                info.Succeeded = false;
            }

            rowFilters.Clear();

            return info;
        }

        private void UpdateExportInfo(ExportInfo info)
        {
            //if (info.Succeeded)
            //{
            //    Properties.Settings.Default.DateLastExport = DateTime.Now;
            //    Properties.Settings.Default.UserLastExport = System.Security.Principal.WindowsIdentity.GetCurrent().Name.ToString();
            //    Properties.Settings.Default.RecordsLastExport = info.TotalRecordsPackaged;
            //    Properties.Settings.Default.Save();

            //    SetStatusMessage("Export complete. " + info.TotalRecordsPackaged.ToString() + " records exported.");
            //}

            if (info.Succeeded)
            {
                Properties.Settings.Default.DateLastExport = info.ExportInitiated;
                Properties.Settings.Default.UserLastExport = System.Security.Principal.WindowsIdentity.GetCurrent().Name.ToString();
                Properties.Settings.Default.RecordsLastExport = info.TotalRecordsPackaged;
                Properties.Settings.Default.Save();
                SetStatusMessage("Export complete. " + info.TotalRecordsPackaged.ToString() + " records exported.");
            }
            else
            {
                SetStatusMessage("Export failed. " + info.TotalRecordsPackaged.ToString() + " records exported.");
            }

            exportProgressBar.Value = 0;
            btnExport.IsEnabled = true;
            listboxExportTypes.IsEnabled = true;
        }

        private void btnOpenFolder_Click(object sender, RoutedEventArgs e)
        {
            FileInfo fi = new FileInfo(DataHelper.Project.FilePath);
            DirectoryInfo di = new DirectoryInfo(System.IO.Path.Combine(fi.Directory.FullName, Properties.Settings.Default.DataExportFolder));
            System.Diagnostics.Process.Start("explorer.exe", di.FullName);
        }

        private void btnClean_Click(object sender, RoutedEventArgs e)
        {
            FileInfo fi = new FileInfo(DataHelper.Project.FilePath);
            DirectoryInfo di = new DirectoryInfo(System.IO.Path.Combine(fi.Directory.FullName, Properties.Settings.Default.DataExportFolder));

            DateTime now = DateTime.Now;
            DateTime minusThirty = now.AddDays(-30);

            List<FileInfo> filesToDelete = new List<FileInfo>();

            foreach (var file in di.GetFiles("*.edp7"))
            {
                if (file.CreationTime < minusThirty)
                {
                    filesToDelete.Add(file);
                }
            }

            if (filesToDelete.Count > 0)
            {
                MessageBoxResult result = MessageBox.Show(filesToDelete.Count.ToString() + " transmission files are more than 30 days old and will be permanently deleted. Proceed?", "Proceed", MessageBoxButton.YesNo, MessageBoxImage.Question);
                if (result == MessageBoxResult.Yes)
                {
                    foreach (FileInfo fileInfo in filesToDelete)
                    {
                        try
                        {
                            fileInfo.Delete();
                        }
                        catch
                        {
                        }
                    }
                }
            }
            else
            {
                MessageBox.Show("No transmission files are more than 30 days old. No files will be purged", "Information", MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }

        private void grdHome_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            ResetHeight();
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            if (Properties.Settings.Default.SystemLevel == 1)
            {
                panelLabExportType.Visibility = System.Windows.Visibility.Visible;
            }
            else
            {
                panelLabExportType.Visibility = System.Windows.Visibility.Collapsed;
            }
        }
    }
}
